﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Puzzle
{
    [Serializable]
    public partial class Population
    {
        public enum mutateMode { simple, block, block_conserveEdges, conserveEdges };
        public List<Board> boards;
        public Board sovled;
        //public Board testee;
        

        public int readInSolved(string path = "C:\\Users\\Icarus\\Desktop\\10width7pats.original")
        {

            string fileInput = File.ReadAllText(path);
            string[] splitInputTemp = fileInput.Split(new Char[] { ' ' });
            List<string> splitInput = new List<string>();
            foreach (string yobro in splitInputTemp)
            {
                if (!(yobro == "") && !(yobro == "\n"))
                    splitInput.Add(yobro);

            }
            this.sovled = new Board(splitInput);
            return splitInput.Count;
        }
        public List<int> readInRandom(string dirPath = "C:\\Users\\Icarus\\Desktop\\10width7pats")
        {
            List<int> ret = new List<int>(); //first int returned is number of files read, second is how many patterns (numner of patters = tiles *4) per file
            int count = 0;
            int numOfPatterns = -1;

            this.boards = new List<Board>();

            IEnumerable<string> fls = Directory.EnumerateFiles(dirPath);
            foreach (string f in fls)
            {
                count++;
                string fileInput = File.ReadAllText(f);
                string[] splitInputTemp = fileInput.Split(new Char[] { ' ' });
                List<string> splitInput = new List<string>();
                foreach (string yobro in splitInputTemp)
                {
                    if (!(yobro == "") && !(yobro == "\n"))
                        splitInput.Add(yobro);

                }
                if (numOfPatterns == -1)
                    numOfPatterns = splitInput.Count;
                else if (numOfPatterns != splitInput.Count)
                    throw new CustomExc("In readInRandom(), files do not all contain the same number of patterns");
  
                this.boards.Add(new Board(splitInput));
            }

            ret.Add(count);
            ret.Add(numOfPatterns);
            return ret;
        }
        public int quadrupledPopulation()
        {
            int currentPop = this.boards.Count;
            for (int i = 0; i < currentPop; i++)
            {
                this.boards.Add(Gen.DeepClone<Board>(this.boards[i]));
                this.boards.Add(Gen.DeepClone<Board>(this.boards[i]));
                this.boards.Add(Gen.DeepClone<Board>(this.boards[i]));
            }

            return this.boards.Count;
        }
        
        public int doublePopulation()
        {
            int currentPop = this.boards.Count;
            for (int i = 0; i < currentPop; i++)
            {
                this.boards.Add(Gen.DeepClone<Board> (this.boards[i]));
            }

            return this.boards.Count;
        }
        public void selectPopulation2x()
        {
            List<long> allScores = new List<long>();
            List<Board> newBoards = new List<Board>();
            foreach (Board b in this.boards)
            {
                allScores.Add(b.fitness);
            }

            allScores.Sort();

            long medianScore = allScores[49];

            if (this.boards.Count != 200)
                throw new CustomExc("In selectPopulation, before the culling actually happened, the population is not exactly 100 boards");

            for (int j = 0; j < this.boards.Count; j++)
            {

                if (this.boards[j].fitness >= medianScore)
                    newBoards.Add(this.boards[j]);
                if (newBoards.Count == 50)
                    break;

            }

            if (newBoards.Count != 50)
                throw new CustomExc("In selectPopulation, after the culling, there are not 50 members left");
            this.boards = newBoards;




        }
        public void selectPopulation()
        {
            List<long> allScores = new List<long>();
            List<Board> newBoards = new List<Board>();
            foreach (Board b in this.boards)
            {
                allScores.Add(b.fitness);
            }

            allScores.Sort();

            long medianScore = allScores[49];

            if (this.boards.Count != 100)
                throw new CustomExc("In selectPopulation, before the culling actually happened, the population is not exactly 100 boards");
            
            for (int j = 0; j < this.boards.Count; j++)
            {
                
                if (this.boards[j].fitness >= medianScore)
                    newBoards.Add(this.boards[j]);
                if (newBoards.Count == 50)
                    break;

            }

            if (newBoards.Count != 50)
                throw new CustomExc("In selectPopulation, after the culling, there are not 50 members left");
            this.boards = newBoards;


        }
        public void mutate(mutateMode mutMode, uint iterations, uint minimumSidesForMutation = 0)
        {
            if (mutMode == mutateMode.simple)
                mutateSimple(iterations);
            else if (mutMode == mutateMode.conserveEdges)
                mutate_conserveEdges(iterations, minimumSidesForMutation);
            else if (mutMode == mutateMode.block)
                return;
            else if (mutMode == mutateMode.block_conserveEdges)
                return;


        }
        public void mutateSimple(uint iterations)
        {
                foreach (Board b in this.boards)
                {
                    b.swapRotateSinglePiece(iterations);
                }

        }

        public void rotateAllEdgesAndCornersCorrectly()
        {
            foreach(Board b in this.boards)
            {
                //correctly align edges and corners
                //b.outputBoard("C:\\Users\\Icarus\\Desktop\\" + b.fitness.ToString());
                b.rotateAllCorners();
                b.rotateAllEdges();
                //b.outputBoard("C:\\Users\\Icarus\\Desktop\\" + b.fitness.ToString() + "_2");
            }
        }
        public void mutate_conserveEdges(uint iterations)
        {
            int i = 0;

            foreach (Board b in this.boards)
            {
                //Console.WriteLine("Mutating board: " + i.ToString());
                b.swapRotate_ConserveEdgeIntegrity(iterations);
                i++;
                //Console.Clear();
            }
        }

        public void mutate_conserveEdges(uint iterations, uint minimumMathcingNeighbors)
        {
            foreach (Board b in this.boards)
                b.swapRotate_ConserveEdgeIntegrity(iterations);

        }
        public void slide2x2Blocks(uint attempts)
        {
            foreach (Board b in this.boards)
            {
                b.slideBlock2x2OfMatchingPieces(attempts);
            }
        }
        public void evalFitness()
        {
            foreach (Board b in this.boards)
            {
                b.fitnessEvaluation();
            }
        }

        public void displayAllBoardsFitness()
        {
            
            foreach (Board b in this.boards)
            {
                Console.WriteLine(b.fitness + "     " + (b.approxTilesArranged() / 10).ToString());
            }

        }

        public void mutateOnlyCenter(uint iterations, uint minNeighnor)
        {
            foreach (Board b in this.boards)
                b.swapRotateOnlyMiddlePiecesFewNeighbors(iterations, minNeighnor);
        }

        public void mutateOnlyEdge(uint iterations)
        {
            foreach (Board b in this.boards)
                b.swapOnlyEdgePieces(iterations);
        }

        public void mutateOnlyCorner(uint iterations)
        {
            foreach (Board b in this.boards)
                b.swapOnlyCornerPieces(iterations);
        }

        public void writeFitnessToFile(int iteration,string destination = "C:\\Users\\Icarus\\Desktop\\boardFitnesses_2ndtake.txt")
        {
            Random ran = new Random();

            string output = "  " + iteration.ToString() + "    Solved:" + this.sovled.fitnessEvaluation().ToString() + "\n";
            foreach (Board b in this.boards)
            {
                output += b.fitnessEvaluation().ToString() + "  " + (b.approxTilesArranged() / 10).ToString() + "\n";
                //b.outputBoard("C:\\Users\\Icarus\\Desktop\\" + (b.approxTilesArranged()/10).ToString() + " " + ran.Next());
            }

            output += "\n";
            File.AppendAllText(destination, output);
        }


    }
}
